﻿Option Explicit On

Imports System.IO
Imports VPager.WebService
Imports log4net
Imports VPager.Exceptions

''' <summary>
''' The PageList class implements a circular buffer of page codes to be displayed.
''' </summary>
''' <remarks>
''' </remarks>

Public Class PageList

    ' -------------------------------------------------------------------------
    ' Constants
    ' -------------------------------------------------------------------------

    ' -------------------------------------------------------------------------
    ' Static Variables
    ' -------------------------------------------------------------------------
    Private Shared sLog As ILog = Nothing

    ' -------------------------------------------------------------------------
    ' Member Variables
    ' -------------------------------------------------------------------------
    Private mData As List(Of PageItem) = Nothing
    Private mIndex As Integer
    Private mLastEventTime As Date
    Private mLastPageID As Long = 0
    Private mTickStart As Long

    ' -------------------------------------------------------------------------
    ' Property Definitions
    ' -------------------------------------------------------------------------
    '

    ' -------------------------------------------------------------------------
    ' Event Definitions
    ' -------------------------------------------------------------------------
    '
    Public Event Changed(ByVal sender As PageList, ByVal e As EventArgs)

    Protected Overridable Sub OnChanged(ByVal e As EventArgs)
        RaiseEvent Changed(Me, e)
    End Sub


    ' -------------------------------------------------------------------------
    ' Main Code
    ' -------------------------------------------------------------------------

    ''' <summary>
    ''' Constructor. 
    ''' </summary>
    Public Sub New()
        If IsNothing(sLog) Then
            sLog = LogManager.GetLogger(Me.GetType)
        End If

        mIndex = 0
        mTickStart = DateTime.UtcNow.Ticks
        mData = New List(Of PageItem)
    End Sub

    ''' <summary>
    ''' Returns the contents of the Page List as a PageItem array.
    ''' </summary>
    Public Function getList() As PageItem()
        SyncLock mData
            Return mData.ToArray()
        End SyncLock
    End Function

    ''' <summary>
    ''' Returns number of individual page codes in the current list.
    ''' </summary>
    Public Function getCount() As Integer
        SyncLock mData
            Return mData.Count
        End SyncLock
    End Function


    Public Function isEmpty() As Boolean
        Return (getCount() = 0)
    End Function

    ''' <summary>
    ''' Returns the next item in the Page List and advances the internal list pointer.  
    ''' As PageList implements a circular buffer, subsequent calls will wrap around the end 
    ''' of list and continue from the beginning of the list.
    ''' </summary>
    Public Function nextDisplayableItem() As PageItem

        Dim page As PageItem

        SyncLock mData
            Do
                page = Me.peek()
                If page IsNot Nothing Then
                    mIndex = mIndex + 1
                End If
            Loop Until ((page Is Nothing) OrElse (TypeOf page Is ParentPage))
        End SyncLock

        Return page

    End Function

    ''' <summary>
    ''' Returns the next item in the list without advancing the internal list pointer.
    ''' </summary>
    Public Function peek() As PageItem

        Dim listSize As Integer
        Dim rv As PageItem

        SyncLock mData
            listSize = mData.Count
            rv = Nothing

            If listSize > 0 Then
                If (mIndex > listSize - 1) Then
                    mIndex = 0
                End If

                rv = mData.Item(mIndex)

            End If
        End SyncLock

        Return rv

    End Function

    ''' <summary>
    ''' Clears the page list contents until the nest refresh() is called.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub clear()
        SyncLock mData
            mData.Clear()
        End SyncLock
    End Sub

    ''' <summary>
    ''' Returns the contents of the Page List as a string of space seperated codes.
    ''' </summary>
    Public Overrides Function toString() As String
        Dim rv As String = ""

        SyncLock mData
            For Each page As PageItem In mData
                If rv = "" Then
                    rv = page.Primary
                Else
                    rv = rv & ", " & page.Primary
                End If
            Next
        End SyncLock

        Return rv
    End Function

    Public Function requestPageAdd(ByVal page As PageItem) As PageItem
        Dim existingPage As PageItem = Nothing

        If (page.Validate()) Then
            existingPage = getMatchingPage(page)
            If (IsNothing(existingPage)) Then
                page.ID = getNextPageID()
                page.CreationTime = DateTime.Now
                SyncLock mData
                    mData.Add(page)
                End SyncLock

                sLog.Debug("New Page Added: " + page.ToString())
                RaiseEvent Changed(Me, New EventArgs())
            Else
                'A similar (matching) page exits - throw error
                Throw New PageExistsException(
                    "A page already exists with Primary value: " + page.Primary)
            End If
        Else
            Throw New ArgumentException("PageRequest must have a Code value.")
        End If

        Return page

    End Function

    Private Function getMatchingPage(ByVal template As PageItem) As PageItem
        Dim foundPage As PageItem = Nothing

        For Each page As PageItem In mData
            If page.SameAs(template) Then
                foundPage = page
                Exit For
            End If
        Next

        Return foundPage

    End Function

    Public Function getPageByID(ByVal pageID As Long) As PageItem
        Dim page As PageItem = Nothing
        If (pageID > 0) Then

            SyncLock mData
                page = mData.Find(Function(value As PageItem)
                                      Return value.ID = pageID
                                  End Function)
            End SyncLock

        End If

        Return page

    End Function

    Public Function pageDelete(ByVal pageID As Long) As Boolean
        Dim page As PageItem = getPageByID(pageID)
        If (Not IsNothing(page)) Then
            SyncLock mData
                mData.Remove(page)
            End SyncLock

        End If

        Return (Not IsNothing(page))

    End Function


    Private Function getNextPageID() As Long
        Dim pid As Long
        Do
            pid = DateTime.UtcNow.Ticks - mTickStart
        Loop Until pid <> mLastPageID
        mLastPageID = pid

        Return pid
    End Function

    Private Sub PageList_Changed(ByVal sender As PageList, ByVal e As System.EventArgs) Handles Me.Changed

    End Sub
End Class
